package org.basis.groovy.loader;

import com.ruoyi.common.utils.spring.SpringUtils;
import org.basis.enhance.groovy.compiler.DynamicCodeCompiler;
import org.basis.enhance.groovy.entity.ScriptEntry;
import org.basis.enhance.groovy.entity.ScriptQuery;
import org.basis.enhance.groovy.loader.ScriptLoader;
import org.basis.groovy.config.properties.GroovyFlowableLoaderProperties;
import org.flowable.bpmn.model.*;
import org.flowable.bpmn.model.Process;
import org.flowable.engine.RepositoryService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.lang.NonNull;
import org.springframework.util.CollectionUtils;
import org.springframework.util.DigestUtils;
import org.springframework.util.StringUtils;

import java.util.*;

/**
 * 从Redis里加载脚本loader
 *
 * @author wenpan 2022/09/25 13:09
 */
public class FlowableScriptLoader implements ScriptLoader {

    private final Logger logger = LoggerFactory.getLogger(getClass());


    private final DynamicCodeCompiler dynamicCodeCompiler;

    private final GroovyFlowableLoaderProperties groovyFlowableLoaderProperties;

    public FlowableScriptLoader(DynamicCodeCompiler dynamicCodeCompiler,
                                GroovyFlowableLoaderProperties groovyFlowableLoaderProperties) {
        this.dynamicCodeCompiler = dynamicCodeCompiler;
        this.groovyFlowableLoaderProperties = groovyFlowableLoaderProperties;
    }

    @Override
    public ScriptEntry load(@NonNull ScriptQuery query) throws Exception {

        String uniqueKey = query.getUniqueKey();
        String[] procIdAndActIdArr = uniqueKey.split(",");
        String procId = procIdAndActIdArr[0];
        String actId = procIdAndActIdArr[1];
        String extProp = procIdAndActIdArr[2];

        // 从flowable的UserTask扩展属性中获取脚本内容
        String script = getExtensionElementValue(procId,actId,extProp);


        if (!StringUtils.hasText(script)) {
            return null;
        }
        // 获取脚本指纹
        String fingerprint = DigestUtils.md5DigestAsHex(script.getBytes());
        // 创建脚本对象
        ScriptEntry scriptEntry = new ScriptEntry(query.getUniqueKey(), script, fingerprint, System.currentTimeMillis());
        // 动态加载脚本为Class
        Class<?> aClass = dynamicCodeCompiler.compile(scriptEntry);
        scriptEntry.setClazz(aClass);
        return scriptEntry;
    }

    @Override
    public List<ScriptEntry> load() {
        List<ScriptEntry> resultList = new ArrayList<>();
        return resultList;
    }


    public static String getExtensionElementValue(String procId,String actId, String extProp) {

        RepositoryService repositoryService = SpringUtils.getBean(RepositoryService.class);
        Collection<FlowElement> list = parseBpmnModelElementWithSubProcess(repositoryService.getBpmnModel(procId));
        FlowElement flowNode = null ;
        for (FlowElement f : list) {
            if (f.getId().equals(actId)) {
                flowNode = f;
                break;
            }
        }

        String retStr = "";
        Map<String, List<ExtensionElement>> extensionMap = flowNode.getExtensionElements();
        if (extensionMap != null && extensionMap.size() > 0) {
            for (Map.Entry<String, List<ExtensionElement>> entry : extensionMap.entrySet()) {
                if(extProp!=null && extProp.equals(entry.getKey())){
                    List<ExtensionElement> extensionList = entry.getValue();
                    if (extensionList != null && extensionList.size() > 0) {
                        ExtensionElement extensionElement = extensionList.get(0);
                        retStr = extensionElement.getElementText();
                    }
                }
            }
        }
        return retStr;
    }


    /**解析BpmnModel所有元素，包括子流程里面的元素*/
    public static  List<FlowElement>  parseBpmnModelElementWithSubProcess(BpmnModel bpmnModel){
        List<FlowElement> retList = new ArrayList<>();
        List<FlowElement> list = (List) bpmnModel.getMainProcess().getFlowElements();
        if (list != null && list.size() > 0) {
            for (int i = 0; i < list.size(); i++) {
                FlowElement obj = list.get(i);
                /**第一层子流程*/
                if (obj instanceof SubProcess) {
                    SubProcess subProcess = (SubProcess) obj;
                    List<FlowElement> listChild = (List<FlowElement>) subProcess.getFlowElements();
                    for (int j = 0; j <listChild.size() ; j++) {
                        FlowElement objChild = listChild.get(j);
                        /**第二层子流程*/
                        if (objChild instanceof SubProcess) {
                            SubProcess subProcess2 = (SubProcess) obj;
                            List<FlowElement> listChild2 = (List<FlowElement>) subProcess2.getFlowElements();
                            for (int p = 0; p <listChild2.size() ; p++) {
                                FlowElement objChild2 = listChild2.get(p);
                                retList.add(objChild2);
                            }
                        }
                        retList.add(objChild);
                    }
                }
                retList.add(obj);
            }
        }
        return retList;
    }



}
